/**
 * 四类操作：
 * 0： 路径异或和
 * 1： 连接x和y，需要先判断是否连通
 * 2： 删除边x和y，需要先判断边是否存在，注意边的存在性判断
 * 3： 单点设置，delta不为零
 * 
 */
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;

struct lct_t{ // link-cut-tree, 1-index, 只能处理路径

using data_type = int;  // 数据类型
using value_type = int; // 维护的信息类型
using lazy_type = int;  // 延迟标记类型

enum {LEFT = 0, RIGHT = 1};

struct node_t{
    int parent;
	int son[2];
	data_type data;   // 点权
	value_type value; // 信息
    int size;         // 伸展树上的size，也是信息之一
	lazy_type lazy;   // 延迟标记
	int flip;      // 翻转标记
};

int N;
vector<node_t> Nodes;
int toUsed;

static value_type mkValue(const data_type & data){
	// assert(0);
    return data;
}

// 从下往上计算value，用于pushUp,伸展树应保证ls与rs均有意义
value_type _up_(const value_type & ls, const value_type & rs) {
	// assert(0);
    return ls ^ rs;
}

// 根据变动信息delta计算本节点的信息并且打延迟标记，用于pushDown或者modify
// flip其实也是延迟标记的一部分，这里只计算t，跟t的儿子无关
// 要维护t的data、value和lazy、flip
void _dn_(int t, const lazy_type & delta, int flip) {
	// assert(0);
    auto & n = Nodes[t];
    if(flip){
        swap(n.son[LEFT], n.son[RIGHT]);
        n.flip ^= 1;
    }
    if(delta){
        n.data = n.value = delta;
    }
    
    return;
}

/// init之后要手工初始化节点并且link
void init(int n){
	// Nodes.assign((N = n) + 1, {0, {0, 0}, data_zero(), value_zero(), 0, lazy_zero(), 0});
    Nodes.clear();
    Nodes.reserve((N = n) + 1);
    toUsed = 0;
	return;
}

int _newNode(const data_type & data){
    memset(Nodes.data()+(++toUsed),0,sizeof(node_t));
    Nodes[toUsed].data = data;
    _pushUp(toUsed);
    return toUsed;
}

// 根据t节点的儿子，计算t节点
void _pushUp(int t) {
	auto & n = Nodes[t];
	n.value = mkValue(n.data);
    n.size = 1;
    int son = n.son[LEFT];
	if(son) {
		n.size += Nodes[son].size;
		n.value = _up_(Nodes[son].value, n.value);
	}
	son = n.son[RIGHT];
	if(son) {
		n.size += Nodes[son].size;
		/// 必须t在左、右儿子在右
		n.value = _up_(n.value, Nodes[son].value);
	}
	return;
}

// 根据t的延迟去计算t的儿子
void _pushDown(int t) {
	auto & n = Nodes[t];
    if(n.lazy == lazy_zero() and n.flip == 0) return;
	
	int son;
	auto & lz = n.lazy;
	auto flip = n.flip;
	if(son = Nodes[t].son[LEFT]) _dn_(son, lz, flip);
	if(son = Nodes[t].son[RIGHT]) _dn_(son, lz, flip);

	lz = lazy_zero();
	n.flip = 0;
	return;
}

/// 判断t是否为本splay的根
/// 父亲为零（真树根?），父亲的左右儿子不是t
bool _isRoot(int t){
    int p = Nodes[t].parent;
    return 0 == p or (Nodes[p].son[LEFT] != t and Nodes[p].son[RIGHT] != t);
}

//获取排行，不能用于树根
int _getSN(int t){
    return Nodes[Nodes[t].parent].son[RIGHT] == t ? RIGHT : LEFT;
}

inline void _link(int p,int sn,int t){
    Nodes[Nodes[t].parent = p].son[sn] = t;
}

inline void _rotate(int t){
    int p = Nodes[t].parent;//父节点
    int g = Nodes[p].parent;//祖父节点
    int sn = _getSN(t);
    int osn = sn ^ 1;
    //不需要pushDown，在splay中pushDown

    _link(p, sn, Nodes[t].son[osn]);
    Nodes[t].parent = g;
    if(not _isRoot(p)) Nodes[g].son[_getSN(p)] = t;
    _link(t,osn,p);

    _pushUp(p);
	return;
}

//从根开始逐级下放延迟标记
void _maintain(int t){
    if(not _isRoot(t)) _maintain(Nodes[t].parent);
    _pushDown(t);
}

void _splay(int t){
    _maintain(t);
    while(!_isRoot(t)){
        int p = Nodes[t].parent;
        if(!_isRoot(p)) _getSN(t)==_getSN(p)?_rotate(p):_rotate(t);
        _rotate(t);
    }
    _pushUp(t);
}

//从根到x形成一条链，返回
int _access(int x){
    int t = 0;
    for(;x;x=Nodes[t=x].parent){
        _splay(x);
        Nodes[x].son[RIGHT] = t;//将链接上
        _pushUp(x);
    }
    return t;
}

//令x成为原树的根
void _makeRoot(int x){
    _access(x);
    _splay(x);
    _dn_(x, lazy_zero(), 1);
	return;
}

//获取x所在树的根节点
int _getRoot(int x){
    _access(x);
    _splay(x);
    while( Nodes[x].son[LEFT] ) x = Nodes[x].son[LEFT];
    _splay(x);
    return x;
}
//令child是parent的子节点
void link(int parent,int child){
    _makeRoot(child);
    Nodes[child].parent = parent; // 注意这里不能用 _link
}

//将child从parent树上切下
void cut(int parent,int child){
    _makeRoot(parent);
    _access(child);
    _splay(child);
    Nodes[child].son[LEFT] = Nodes[Nodes[child].son[LEFT]].parent = 0;
    _pushUp(child);
}
//在root做树根的情况下，x和y的最近公共祖先
int lca(int root,int x,int y){
    _makeRoot(root);
    _access(x);
    return _access(y);
}
bool isConnect(int x,int y){
    return x == y || _getRoot(x) == _getRoot(y);
}

void modify(int x, int y, const lazy_type & delta){
    _makeRoot(x);
    _access(y);
    _splay(y);

	_dn_(y, delta, 0);
	return;
}

value_type query(int x, int y){
    _makeRoot(x);
	_access(y);
	_splay(y);
	return Nodes[y].value;
}

static const data_type & data_zero(){
    static const data_type DATA_0 = 0;
	return DATA_0;
}

static const value_type & value_zero(){
    static const value_type VALUE_0 = 0;
	return VALUE_0;
}

static const lazy_type & lazy_zero(){
    static const lazy_type LAZY_0 = 0;
	return LAZY_0;
}

};


int N;
int Q;
lct_t Tree;

void work(){
    cin >> N >> Q;
    Tree.init(N);
    for(int x,i=1;i<=N;++i){
        cin >> x;
        Tree._pushUp(Tree._newNode(x));
    }
    for(int cmd,a,b,q=1;q<=Q;++q){
        cin >> cmd >> a >> b;
        switch(cmd){
            case 0:{
                auto ans = Tree.query(a, b);
                cout << ans << "\n";
                break;
            }
            case 1:{
                if(not Tree.isConnect(a, b)){
                    Tree.link(a, b);
                }
                break;
            }
            case 2:{
                if(Tree.isConnect(a, b)){
                    Tree._makeRoot(a);
                    Tree._access(b);
                    Tree._splay(b);
                    if(Tree.Nodes[b].size == 2){
                        Tree.Nodes[b].son[lct_t::LEFT] = Tree.Nodes[a].parent = 0;
                        Tree._pushUp(b);
                    }                    
                }

                break;
            }
            case 3:{
                Tree.modify(a, a, b);
                break;
            }
            default: assert(0);
        }
    }     
    return;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int nofkase = 1;
    // cin >> nofkase;
    while(nofkase--) work();
    return 0;
}